using System;
using UnityEngine;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;

public static class UnsafeTool
{
    public static void RefreshPolygonColliderBySprite(GameObject obj,Sprite sprite)
    {
        PolygonCollider2D polygonCollider = obj.GetComponent<PolygonCollider2D>();
     //   Debug.Log(polygonCollider+" "+sprite);
        //for (int i = 0; i < polygonCollider.pathCount; i++) polygonCollider.SetPath(i, default(Vector2[]));
        polygonCollider.pathCount = sprite.GetPhysicsShapeCount();

        List<Vector2> path = new List<Vector2>();
        for (int i = 0; i < polygonCollider.pathCount; i++)
        {
            path.Clear();
            sprite.GetPhysicsShape(i, path);
            polygonCollider.SetPath(i, path.ToArray());
        }
    }
    public static float GetPolygonColliderArea(this PolygonCollider2D polygonCollider)
    {
        float ans = 0;        
        for (int i = 0; i < polygonCollider.pathCount; i++)
        {

            ans+=GetArea(new List<Vector2>( polygonCollider.GetPath(i)).ConvertAll(s=>(Vector3) s));
        }
        return ans;
    }
    public class DeepCopyPair
    {
        public object data;
    }
    public static void CopyGameObject(GameObject obj,GameObject old,bool copyAll)
    {
        Debug.Assert(obj.transform.childCount == old.transform.childCount);
        List<GameObject> ca = new List<GameObject>();
        List<GameObject> cb = new List<GameObject>();
        for(int i = 0; i < obj.transform.childCount; i++)
        {
            ca.Add(obj.transform.GetChild(i).gameObject);
            cb.Add(old.transform.GetChild(i).gameObject);
        }
        Comparison<GameObject> sort = (GameObject a, GameObject b) =>
         {
             if (a.name == b.name)
             {
                 Debug.LogError($"{a.transform.parent.name}��Ҫ������������{a.name}��������ͬ");
                 return 0;

             }
             else
             {
                 return string.Compare(a.name, b.name);
             }
         };
        ca.Sort(sort);
        cb.Sort(sort);
        for(int i = 0; i < ca.Count; i++)
        {
            CopyGameObject(ca[i], cb[i], copyAll);
        }
        var lisa = obj.GetComponents<Component>();
        var lisb = old.GetComponents<Component>();
        Debug.Assert(lisa.Length == lisb.Length);
        for(int i = 0; i < lisb.Length; i++)
        {
            CopyComponentValue(lisa[i], lisb[i], copyAll);
        }
    }
    public static void CopyComponentValue(Component obj, Component old,bool copyAll)
    {
        Debug.Assert(obj.GetType() == old.GetType());
        var type = obj.GetType();
        var fields = new List<FieldInfo>(obj.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic));
        fields.RemoveAll(s => s.IsPublic == false && s.IsDefined(typeof(SerializeField)) == false);
        foreach(var x in fields)
        {
            if (x.FieldType.IsValueType)
            {
                x.SetValue(obj, x.GetValue(old));   
            }else if (x.FieldType.IsDefined(typeof(SerializableAttribute)))
            {
                var v = x.GetValue(old);
                if(v != null)
                {
                    v = UnsafeTool.DeepCopyByJson(v);
                }
                x.SetValue(obj, v);
            }
        }
        if(copyAll == false)
        {
            return;
        }
        var props=new List<PropertyInfo>(obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance ));
        foreach(var x in props)
        {
            if (x.PropertyType.IsValueType)
            {
                x.SetValue(obj, x.GetValue(old));
            }
            else if (x.PropertyType.IsDefined(typeof(SerializableAttribute)))
            {
                var v = x.GetValue(old);
                if (v != null)
                {
                    v = UnsafeTool.DeepCopyByJson(v);
                }
                x.SetValue(obj, v);
            }
        }  
    }
    public static object DeepCopyByJson(object obj)
    {
        Debug.Assert(obj != null);
        if (obj is string || obj.GetType().IsValueType) return obj;

        return JsonUtility.FromJson(JsonUtility.ToJson(obj), obj.GetType());
    }
    public static T DeepCopy<T>(T obj)
    {
        //������ַ�����ֵ������ֱ�ӷ���
        if (obj is string || obj.GetType().IsValueType) return obj;

        object retval = Activator.CreateInstance(obj.GetType());
        FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
        foreach (FieldInfo field in fields)
        {
            try { field.SetValue(retval, DeepCopy(field.GetValue(obj))); }
            catch { }
        }
        return (T)retval;
    }
    public static Vector2 TurnAngle(Vector2 p, float a)
    {
        return new Vector2(p.x * Mathf.Cos(a) - p.y * Mathf.Sin(a), p.x * Mathf.Sin(a) + p.y * Mathf.Cos(a));

    }
    public static Vector2 GetPositiveDirectProjection(Vector2 p, Vector2 direction)
    {
        Vector2 tp = direction.normalized * Vector2.Dot(p, direction) / direction.magnitude;
        if (Vector2.Dot(tp, p) < 0.0f) tp = -tp;
        return tp;
    }
    public static Vector2 TurnToDirection(Vector2 p, Vector2 direction)
    {
        Vector2 tp = direction.normalized * p.magnitude;
        return tp;
    }
    public static float GetDisToLine(Vector2 p, Vector2 l, Vector2 r)
    {
        return Mathf.Abs(Vector3.Cross(p - l, r - l).magnitude / (r - l).magnitude);

    }
    public static bool CheckPointInLine2D(Vector3 p, Vector3 l, Vector3 r, float lineWidth)
    {
        //   return Vector3.Cross(p - l, r - l).magnitude / (r - l).magnitude;
        p.z = l.z = r.z = 0;
        if (Vector3.Dot(p - l, r - l) * Vector3.Dot(p - r, l - r) < 0)
        {
            return false;
        }
        if (GetDisToLine(p, l, r) * 2 > lineWidth)
        {
            return false;
        }
        return true;
    }
    public static bool IsRectanInArea2D(Vector3 p, Vector2 size, List<Vector3> area)
    {
        Debug.Assert(area.Count > 2);
        List<Vector3> Rec = GetRectBoundPoint(p, size);
        return Mathf.Abs(GetTwoAreaIntersection(Rec, area) - size.x * size.y) < 0.01f;
    }
    public static List<Vector3> GetAreaRectBound(List<Vector3> area)
    {
        Vector2 mx = new Vector2(float.NegativeInfinity, float.NegativeInfinity), mn = new Vector2(float.PositiveInfinity, float.PositiveInfinity);
        foreach (var p in area)
        {

            mn.x = Mathf.Min(p.x, mn.x);
            mn.y = Mathf.Min(p.y, mn.y);
            mx.x = Mathf.Max(p.x, mx.x);
            mx.y = Mathf.Max(p.y, mx.y);

        }
        List<Vector3> ans = new List<Vector3>();
        ans.Add(mn);
        ans.Add(new Vector2(mn.x, mx.y));
        ans.Add(mx);
        ans.Add(new Vector2(mx.x, mn.y));
        return ans;
    }
    public static Bounds GetBounds(List<Vector3> area)
    {
        var tp = GetAreaRectBound(area);
        return new Bounds((tp[0] + tp[2]) / 2, tp[2] - tp[0]);
    }
    public static List<Vector3> GetRectBoundPoint(Vector2 center, Vector2 size, float angle = 0f)
    {
        List<Vector3> BoundPoint = new List<Vector3>();
        float[] BoundPos = new float[4];
        BoundPos[0] = center.x + size.x / 2f;
        BoundPos[1] = center.y + size.y / 2f;
        BoundPos[2] = center.x - size.x / 2f;
        BoundPos[3] = center.y - size.y / 2f;
        BoundPoint.Clear();
        for (int i = 0; i < 4; i += 2)
        {
            for (int j = (i == 0 ? 1 : 3); j < 4 && j >= 0; j += (i == 0 ? 2 : -2))
            {
                BoundPoint.Add(new Vector2(BoundPos[i], BoundPos[j]));
            }
        }
        for (int i = 0; i < BoundPoint.Count; i++)
        {
            BoundPoint[i] = TurnAngle((Vector2)BoundPoint[i] - center, angle) + center;
        }
        return BoundPoint;
    }

   /* public static void ModifyCenter(GameObject obj)
    {
        Vector2 center = Vector2.zero;
        Debug.Assert(obj.GetComponent<SpriteShapeController>());
        Spline spline = obj.GetComponent<SpriteShapeController>().spline;
        for (int i = 0; i < spline.GetPointCount(); i++)
        {
            center = center + (Vector2)(spline.GetPosition(i));
        }
        center.x /= spline.GetPointCount();
        center.y /= spline.GetPointCount();
        List<Vector2> pts = new List<Vector2>();
        for (int i = 0; i < spline.GetPointCount(); i++)
        {
            pts.Add((Vector2)spline.GetPosition(i) - center);
        }
        obj.transform.position = obj.transform.position + (Vector3)center;
        while (true)
        {
            int cnt = 0;
            for (int i = 0; i < spline.GetPointCount(); i++)
            {
                if (SpriteShapeEditorTool.Instance.TrySetSplinePoint(spline, i, pts[i]))
                {
                    cnt++;
                }
            }
            if (cnt == spline.GetPointCount())
            {
                break;
            }
        }

    }*/
    public static Vector3 GetNearestPointInPolygen(List<Vector3> A, Vector3 p)
    {
        List<Vector3> anslist = new List<Vector3>();
        for (int i = 0; i < A.Count; i++)
        {
            Vector3 now = A[i], nxt = A[(i + 1) % A.Count];
            if (Vector3.Dot(p - now, nxt - now) * Vector3.Dot(p - nxt, now - nxt) < 0)
            {
                if ((p - now).magnitude < (p - nxt).magnitude)
                {
                    anslist.Add(now);
                }
                else
                {
                    anslist.Add(nxt);
                }
            }
            else
            {
                float a = GetDisToLine(p, now, nxt);
                float b = (p - now).magnitude;
                float c = Mathf.Sqrt(b * b - a * a);
                anslist.Add((nxt - now).normalized * c + now);
            }
        }
        float min = float.MaxValue;
        int id = -1;
        for (int i = 0; i < anslist.Count; i++)
        {
            if ((anslist[i] - p).magnitude < min)
            {
                min = (anslist[i] - p).magnitude;
                id = i;
            }
        }
        return anslist[id];
    }

    public static float FloatMod(float a, float b)
    {
        return a - Mathf.Floor(a / b) * b;
    }
    public static bool EqualZero(float a)
    {
        return Mathf.Abs(a) < 1e-1;
    }

    public static float GetTwoAreaIntersection(List<Vector3> A, List<Vector3> B)
    {
        List<Vector2> a = new List<Vector2>(), b = new List<Vector2>();
        foreach (var x in A)
        {
            a.Add(x);
        }
        foreach (var x in B)
        {
            b.Add(x);
        }
        return dbxmjj.intersectArea(a, b);

    }
    /*  public static bool IsPointOnLineRight2D(Vector3 p,Vector3 l,Vector3 r)
      {
          return Vector3.Cross(p - l, r - l).z > 0;
      }*/
    public static float GetArea(List<Vector3> A)
    {
        List<Vector2> a = new List<Vector2>();
        foreach (var x in A)
        {
            a.Add(x);
        }
        return Mathf.Abs(dbxmjj.area(a));

    }

    public static float GetArea(Vector3[] A)
    {
        List<Vector2> a = new List<Vector2>();
        foreach (var x in A)
        {
            a.Add(x);
        }
        return Mathf.Abs(dbxmjj.area(a));

    }
    public static float GetArea(Vector2[] A)
    {
        List<Vector2> a = new List<Vector2>();
        foreach (var x in A)
        {
            a.Add(x);
        }
        return Mathf.Abs(dbxmjj.area(a));

    }
    /// <summary>
    /// ������ÿһλ�������
    /// </summary>
    /// <param name="a"></param>
    /// <param name="b"></param>
    /// <returns></returns>
    public static Vector3 MultiplyEachValue(Vector3 a, Vector3 b)
    {
        return new Vector3(a.x * b.x, a.y * b.y, a.z * b.z);
    }
    /// <summary>
    /// �����������Ĺ�����
    /// </summary>
    static class dbxmjj
    {

        const double eps = 1E-4;
        static int sig(double d)
        {
            int ans = 0;
            if (d > eps) ans++;
            if (d < -eps) ans--;
            return ans;
        }
        private static float cross(Vector2 o, Vector2 a, Vector2 b)
        {
            return (a.x - o.x) * (b.y - o.y) - (b.x - o.x) * (a.y - o.y);
        }

        public static float area(List<Vector2> ps)
        {
            //  List<Vector2> ps = new List<Vector2>();
            //   ps = p;
            //  ps.Add(ps[0]);
            int n = ps.Count;
            float res = 0;
            for (int i = 0; i < n; i++)
            {
                res += ps[i].x * ps[(i + 1) % n].y - ps[i].y * ps[(i + 1) % n].x;
            }
            //    ps.Remove(ps.Last());
            return res / 2.0f;
        }
        static int lineCross(Vector2 a, Vector2 b, Vector2 c, Vector2 d, ref Vector2 p)
        {
            float s1, s2;
            s1 = cross(a, b, c);
            s2 = cross(a, b, d);
            if (sig(s1) == 0 && sig(s2) == 0) return 2;
            if (sig(s2 - s1) == 0) return 0;
            p.x = (c.x * s2 - d.x * s1) / (s2 - s1);
            p.y = (c.y * s2 - d.y * s1) / (s2 - s1);
            return 1;
        }
        // ������и�  
        //��ֱ��ab�и�����p���и���������(a,b)����࣬��ԭ�ر����и���  
        //����˻�Ϊһ���㣬Ҳ�᷵��ȥ,��ʱnΪ1 
        static void polygon_cut(List<Vector2> p, Vector2 a, Vector2 b)
        {
            List<Vector2> pp = new List<Vector2>();
            //   int m=0;
            int n = p.Count;
            for (int i = 0; i < n; i++)
            {
                if (sig(cross(a, b, p[i])) > 0) pp.Add(p[i]);
                if (sig(cross(a, b, p[i])) != sig(cross(a, b, p[(i + 1) % n])))
                {
                    Vector2 tp = new Vector2();
                    lineCross(a, b, p[i], p[(i + 1) % n], ref tp);
                    pp.Add(tp);
                }
            }
            p.Clear();
            //   n=0;  
            for (int i = 0; i < pp.Count; i++)
                if (i == 0 || !(pp[i] == pp[i - 1]))
                    p.Add(pp[i]);
            while (p.Count > 1 && p.Last() == p[0]) p.Remove(p.Last());
        }
        static void swap<T>(ref T a, ref T b) where T : struct
        {
            T c = a;
            a = b;
            b = c;
        }
        static float fabs(float v)
        {
            return v < 0f ? -v : v;
        }
        //---------------�����ķָ���-----------------//  
        //����������oab��������ocd���������,o��ԭ��//  
        static float intersectArea(Vector2 a, Vector2 b, Vector2 c, Vector2 d)
        {
            Vector2 o = new Vector2(0, 0);
            int s1 = sig(cross(o, a, b));
            int s2 = sig(cross(o, c, d));
            if (s1 == 0 || s2 == 0) return 0.0f;//�˻������Ϊ0  
            if (s1 == -1) swap(ref a, ref b);
            if (s2 == -1) swap(ref c, ref d);
            List<Vector2> p = new List<Vector2> { o, a, b };
            polygon_cut(p, o, c);
            polygon_cut(p, c, d);
            polygon_cut(p, d, o);
            float res = fabs(area(p));
            if (s1 * s2 == -1) res = -res; return res;
        }
        //��������εĽ����  
        static public float intersectArea(List<Vector2> ps1, List<Vector2> ps2)
        {
            if (area(ps1) < 0) ps1.Reverse();// reverse(ps1,ps1+n1);  
            if (area(ps2) < 0) ps2.Reverse();
            int n1 = ps1.Count, n2 = ps2.Count;
            float res = 0;
            for (int i = 0; i < n1; i++)
            {
                for (int j = 0; j < n2; j++)
                {
                    res += intersectArea(ps1[i], ps1[(i + 1) % n1], ps2[j], ps2[(j + 1) % n2]);
                }
            }
            return res;//assumeresispositive!  
        }
    }
}